package cyou.breathe.blog.rpc.facade;

import com.alibaba.fastjson2.JSON;
import cyou.breathe.blog.base.exception.BizException;
import cyou.breathe.blog.base.exception.SystemException;
import cyou.breathe.blog.base.response.BaseResponse;
import cyou.breathe.blog.base.response.ResponseCode;
import cyou.breathe.blog.base.utils.BeanValidator;
import jakarta.validation.ValidationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author: breathe
 * @createTime: 2025-08-26
 */
@Aspect
@Component
public class FacadeAspect {

    private static Logger LOG = LoggerFactory.getLogger(FacadeAspect.class);

    @Around("@annotation(cyou.breathe.blog.rpc.facade.Facade)")
    public Object facade(ProceedingJoinPoint pjp) throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Object[] args = pjp.getArgs();

        LOG.info("开始执行, method = {}, args = {}", method.getName(), JSON.toJSONString(args));
        Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType();

        for (Object param : args) {
            try {
                BeanValidator.validateObject(param, returnType);
            } catch (ValidationException e) {
                printLog(stopWatch, method, args, "校验失败", null, e);
                return getFailedResponse(returnType, e);
            }
        }

        try {
            Object response = pjp.proceed();
            //对响应信息进行补充
            enrichObject(response);
            //打印日志
            printLog(stopWatch, method, args, "结束执行", response, null);
            return response;
        } catch (Throwable throwable) {
            printLog(stopWatch, method, args, "执行失败", null, throwable);
            return getFailedResponse(returnType, throwable);
        }
    }

    private void enrichObject(Object response) {
        if (response instanceof BaseResponse) {
            if (((BaseResponse) response).getSuccess()) {
                //如果状态是成功的，需要将未设置的 responseCode设置成SUCCESS
                if (StringUtils.isEmpty(((BaseResponse) response).getResponseCode())) {
                    //状态响应成功，但是响应码为空，则帮其把响应码进行补全
                    ((BaseResponse) response).setResponseCode(ResponseCode.SUCCESS.name());
                }
            } else {
                //如果状态是失败的，需要将未设置的responseCode设置成BIZ_ERROR
                if (StringUtils.isEmpty(((BaseResponse) response).getResponseCode())) {
                    //状态响应失败，但是响应码为空，则帮其把响应码进行补全
                    ((BaseResponse) response).setResponseCode(ResponseCode.BIZ_ERROR.name());
                }
            }
        }
    }

    private String getInfoMessage(String action, StopWatch stopWatch, Method method, Object[] args, Object response,
                                  Throwable exception) {

        StringBuilder stringBuilder = new StringBuilder(action);
        stringBuilder.append(" ,方法 = ");
        stringBuilder.append(method.getName());
        stringBuilder.append(" ,耗时 = ");
        stringBuilder.append(stopWatch.getTime()).append(" ms");
        if (response instanceof BaseResponse) {
            stringBuilder.append(" ,成功 = ");
            stringBuilder.append(((BaseResponse) response).getSuccess());
        }
        if (exception != null) {
            stringBuilder.append(" ,成功 = ");
            stringBuilder.append(false);
        }
        stringBuilder.append(" ,参数 = ");
        stringBuilder.append(JSON.toJSONString(Arrays.toString(args)));

        if (response != null) {
            stringBuilder.append(" ,响应 = ");
            stringBuilder.append(JSON.toJSONString(response));
        }

        if (exception != null) {
            stringBuilder.append(" ,异常 = ");
            stringBuilder.append(exception.getMessage());
        }

        if (response instanceof BaseResponse) {
            BaseResponse baseResponse = (BaseResponse) response;
            if (!baseResponse.getSuccess()) {
                stringBuilder.append(" , 执行失败");
            }
        }

        return stringBuilder.toString();
    }

    private Object getFailedResponse(Class returnType, Throwable throwable)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        //如果返回值的类型为BaseResponse 的子类，则创建一个通用的失败响应
        if (returnType.getDeclaredConstructor().newInstance() instanceof BaseResponse) {
            BaseResponse response = (BaseResponse) returnType.getDeclaredConstructor().newInstance();
            response.setSuccess(false);
            if (throwable instanceof BizException bizException) {
                response.setResponseMessage(bizException.getErrorCode().getMessage());
                response.setResponseCode(bizException.getErrorCode().getCode());
            } else if (throwable instanceof SystemException systemException) {
                response.setResponseMessage(systemException.getErrorCode().getMessage());
                response.setResponseCode(systemException.getErrorCode().getCode());
            } else {
                response.setResponseMessage(throwable.toString());
                response.setResponseCode(ResponseCode.BIZ_ERROR.name());
            }

            return response;
        }

        LOG.error("获取失败响应失败 , 返回类型 ({}) 不是BaseResponse的子类", returnType);
        return null;
    }


    private void printLog(StopWatch stopWatch, Method method, Object[] args, String action, Object response,
                          Throwable throwable) {
        try {
            //因为此处有JSON.toJSONString，可能会有异常，需要进行捕获，避免影响主干流程
            LOG.info(getInfoMessage(action, stopWatch, method, args, response, throwable), throwable);
            // 如果校验失败，则返回一个失败的response
        } catch (Exception e1) {
            LOG.error("日志打印失败", e1);
        }
    }
}
